package engine.hw;

import engine.config.Config;
import math.Maths;
import net.java.games.input.Component;
import net.java.games.input.Controller;
import net.java.games.input.ControllerEnvironment;

public class Gamepad
{
    
    //<editor-fold defaultstate="collapsed" desc="Singleton">
    private static Gamepad singleton;
    
    public static Gamepad get()
    {
        if (singleton == null)
        {
            singleton = new Gamepad();
        }
        return singleton;
    }
    //</editor-fold>

    public static final float DEAD_ZONE_TRIG = 0.05f;
    public static final float DEAD_ZONE_STICK = 0.23f;
    public static final float 
            DPAD_W = 1.0f,
            DPAD_SW = 0.875f,
            DPAD_S = 0.75f,
            DPAD_SE = 0.625f,
            DPAD_E = 0.5f,
            DPAD_NE = 0.375f,
            DPAD_N = 0.25f,
            DPAD_NW = 0.125f;
    public static final int 
            BUTTON_A = 5, BUTTON_B = 6,
            BUTTON_X = 7, BUTTON_Y = 8,
            BUTTON_LB = 9, BUTTON_RB = 10,
            BUTTON_BACK = 11, BUTTON_START = 12,
            STICK_L = 13, STICK_R = 14;
    private static final int 
            AXIS_Y = 0, AXIS_X = 1,
            ROTATION_Y = 2, ROTATION_X = 3,
            AXIS_Z = 4,
            DPAD = 15;
    
    
    private Controller gamepad;
    private Component[] buttons;
    private boolean[] buttonState;
    private boolean[] buttonPrev;
    
    private float gamepadSensX;
    private float gamepadSensY;
    //<editor-fold defaultstate="collapsed" desc="getSensitivity[X/Y]">
    public float getSensitivityX()
    {
        return this.gamepadSensX;
    }
    
    public float getSensitivityY()
    {
        return this.gamepadSensY;
    }
    //</editor-fold>
    
    private boolean active;
    //<editor-fold defaultstate="collapsed" desc="isActive">
    public boolean isActive()
    {
        return this.active;
    }
    //</editor-fold>
    
    private Gamepad()
    {
        this.buttonState = new boolean[10];
        this.buttonPrev = new boolean[10];
    }
    
    public void initialize(Config cfgFile)
    {
        this.gamepad = this.getFirstGamePad();
        this.active = (this.gamepad != null);
        if (this.active)
        {
            this.buttons = this.gamepad.getComponents();
            this.gamepadSensX = cfgFile.getFloat("GamepadSensitivityHorizontal");
            this.gamepadSensY = cfgFile.getFloat("GamepadSensitivityVertical");
        }
    }

    public void update()
    {
        this.gamepad.poll();
        for (int i = 0; i < this.buttonState.length; i++)
        {
            int buttonInd = i + 5;
            boolean isDown = this.isButtonDown(buttonInd);
            this.buttonPrev[i] = this.buttonState[i]; // Copy to PREV STATE
            this.buttonState[i] = isDown; // Update CURRENT STATE
        }
    }

    public boolean wasPressed(int button)
    {
        int buttonInd = button - 5;
        boolean wasPressed = (
            this.buttonState[buttonInd] && 
            !this.buttonPrev[buttonInd]
        );
        return wasPressed;
    }

    public boolean wasReleased(int button)
    {
        int buttonInd = button - 5;
        boolean wasReleased = (
            !this.buttonState[buttonInd] && 
            this.buttonPrev[buttonInd]
        );
        return wasReleased;
    }

    public boolean isButtonDown(int button)
    {
        return (this.getRawButtonData(button) == 1);
    }

    public boolean isDPadPressed(float buttonVal)
    {
        return (this.getRawButtonData(Gamepad.DPAD) == buttonVal);
    }

    public boolean isLeftTriggerDown()
    {
        return (this.getLeftTriggerValue() > 0);
    }

    public boolean isRightTriggerDown()
    {
        return (this.getRightTriggerValue() > 0);
    }

    public float getLeftStickXValue()
    {
        return this.processStickData(Gamepad.AXIS_X);
    }

    public float getLeftStickYValue()
    {
        return this.processStickData(Gamepad.AXIS_Y);
    }

    public float getRightStickXValue()
    {
        return this.processStickData(Gamepad.ROTATION_X);
    }

    public float getRightStickYValue()
    {
        return this.processStickData(Gamepad.ROTATION_Y);
    }

    public float getRightTriggerValue()
    {
        float data = this.getRawButtonData(Gamepad.AXIS_Z);
        if (Maths.abs(data) < Gamepad.DEAD_ZONE_TRIG)
        {
            return 0;
        }
        else
        {
            if (data >= 0)
            {
                return 0;
            }
            else
            {
                return -data;
            }
        }
    }

    public float getLeftTriggerValue()
    {
        float data = this.getRawButtonData(Gamepad.AXIS_Z);
        if (Maths.abs(data) < Gamepad.DEAD_ZONE_TRIG)
        {
            return 0;
        }
        else
        {
            if (data > 0)
            {
                return data;
            }
            else
            {
                return 0;
            }
        }
    }

    public float getRawButtonData(int index)
    {
        return this.buttons[index].getPollData();
    }

    private float processStickData(int stickVal)
    {
        float rawData = this.getRawButtonData(stickVal);
        float deadData;
        float transData;
        float scaledData;
        if (Maths.abs(rawData) < Gamepad.DEAD_ZONE_STICK)
        {
            deadData = 0;
        }
        else
        {
            deadData = rawData;
        }
        if (deadData > 0)
        {
            transData = deadData - Gamepad.DEAD_ZONE_STICK;
        }
        else
        {
            if (deadData < 0)
            {
                transData = deadData + Gamepad.DEAD_ZONE_STICK;
            }
            else
            {
                transData = deadData;
            }
        }
        scaledData = transData / (1.0f - Gamepad.DEAD_ZONE_STICK);
        return scaledData;
    }

    private Controller getFirstGamePad()
    {
        Controller[] cntrls = (
            ControllerEnvironment.getDefaultEnvironment().getControllers()
        );
        for (Controller cntrl : cntrls)
        {
            if (cntrl.getType() == Controller.Type.GAMEPAD)
            {
                return cntrl;
            }
        }
        return null;
    }

    public void destroy()
    {
        this.gamepad = null;
        this.buttons = null;
        this.buttonState = null;
        this.buttonPrev = null;
    }
    
}
